import java.io.*;
import java.net.*;
import java.util.Scanner;

/**
 * Network Programming Examples
 * This class demonstrates various network programming concepts in Java including:
 * - InetAddress usage
 * - TCP client-server communication
 * - UDP communication
 */
public class NetworkProgramming {

    public static void main(String[] args) {
        NetworkProgramming example = new NetworkProgramming();
        
        // Demonstrating network basics
        example.demonstrateInetAddress();
        
        System.out.println("=== Network Programming Examples ===\n");
        
        // Menu for running different examples
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("Select an example to run:");
            System.out.println("1. TCP Client-Server Example");
            System.out.println("2. UDP Client-Server Example");
            System.out.println("3. Exit");
            System.out.print("Enter your choice: ");
            
            int choice = scanner.nextInt();
            scanner.nextLine(); // Consume newline
            
            switch (choice) {
                case 1:
                    example.runTCPServerClient();
                    break;
                case 2:
                    example.runUDPServerClient();
                    break;
                case 3:
                    System.out.println("Exiting...");
                    scanner.close();
                    return;
                default:
                    System.out.println("Invalid choice. Please try again.");
            }
        }
    }
    
    /**
     * Method to demonstrate InetAddress class
     */
    public void demonstrateInetAddress() {
        System.out.println("=== InetAddress Examples ===");
        
        try {
            // Get InetAddress by host name
            InetAddress address = InetAddress.getByName("www.google.com");
            System.out.println("Google InetAddress: " + address);
            System.out.println("Host Name: " + address.getHostName());
            System.out.println("Host Address: " + address.getHostAddress());
            
            // Get local host InetAddress
            InetAddress localhost = InetAddress.getLocalHost();
            System.out.println("Local Host: " + localhost);
            
            // Get all InetAddress entries for a host
            InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
            System.out.println("All Google addresses:");
            for (InetAddress addr : addresses) {
                System.out.println("  " + addr);
            }
            
        } catch (UnknownHostException e) {
            System.out.println("Error getting InetAddress: " + e.getMessage());
        }
        
        System.out.println();
    }
    
    /**
     * Method to demonstrate TCP client-server communication
     */
    public void runTCPServerClient() {
        System.out.println("=== TCP Client-Server Example ===");
        
        // Start server in a separate thread
        Thread serverThread = new Thread(() -> {
            try {
                startTCPServer();
            } catch (IOException e) {
                System.out.println("Server error: " + e.getMessage());
            }
        });
        
        serverThread.start();
        
        // Wait a bit for server to start
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // Run client
        try {
            startTCPClient();
        } catch (IOException e) {
            System.out.println("Client error: " + e.getMessage());
        }
        
        try {
            serverThread.join(2000); // Wait for server thread to finish
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        System.out.println();
    }
    
    /**
     * TCP Server implementation
     */
    public void startTCPServer() throws IOException {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(8080);
            System.out.println("TCP Server started on port 8080");
            
            // Accept client connection
            Socket clientSocket = serverSocket.accept();
            System.out.println("Client connected: " + clientSocket.getInetAddress());
            
            // Set up input and output streams
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            
            // Read message from client
            String inputLine = in.readLine();
            System.out.println("Received from client: " + inputLine);
            
            // Send response to client
            String outputLine = "Hello from TCP server! Received: " + inputLine;
            out.println(outputLine);
            
            // Close resources
            in.close();
            out.close();
            clientSocket.close();
            
        } finally {
            if (serverSocket != null) {
                serverSocket.close();
            }
        }
        
        System.out.println("TCP Server stopped");
    }
    
    /**
     * TCP Client implementation
     */
    public void startTCPClient() throws IOException {
        Socket socket = null;
        try {
            socket = new Socket("localhost", 8080);
            System.out.println("Connected to server");
            
            // Set up input and output streams
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            
            // Send message to server
            String message = "Hello from TCP client!";
            out.println(message);
            System.out.println("Sent to server: " + message);
            
            // Read response from server
            String response = in.readLine();
            System.out.println("Received from server: " + response);
            
            // Close resources
            in.close();
            out.close();
            
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
        
        System.out.println("TCP Client disconnected");
    }
    
    /**
     * Method to demonstrate UDP client-server communication
     */
    public void runUDPServerClient() {
        System.out.println("=== UDP Client-Server Example ===");
        
        // Start UDP server in a separate thread
        Thread udpServerThread = new Thread(() -> {
            try {
                startUDPServer();
            } catch (IOException e) {
                System.out.println("UDP Server error: " + e.getMessage());
            }
        });
        
        udpServerThread.start();
        
        // Wait a bit for server to start
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // Run UDP client
        try {
            startUDPClient();
        } catch (IOException e) {
            System.out.println("UDP Client error: " + e.getMessage());
        }
        
        try {
            udpServerThread.join(2000); // Wait for server thread to finish
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        System.out.println();
    }
    
    /**
     * UDP Server implementation
     */
    public void startUDPServer() throws IOException {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(8081);
            System.out.println("UDP Server started on port 8081");
            
            // Create buffer for receiving data
            byte[] buffer = new byte[1024];
            
            // Receive packet from client
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            socket.receive(packet);
            
            // Extract message
            String received = new String(packet.getData(), 0, packet.getLength());
            System.out.println("Received from client: " + received);
            
            // Send response back to client
            String response = "Hello from UDP server! Received: " + received;
            byte[] responseData = response.getBytes();
            
            InetAddress clientAddress = packet.getAddress();
            int clientPort = packet.getPort();
            DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length, clientAddress, clientPort);
            socket.send(responsePacket);
            
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
        
        System.out.println("UDP Server stopped");
    }
    
    /**
     * UDP Client implementation
     */
    public void startUDPClient() throws IOException {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket();
            System.out.println("UDP Client started");
            
            // Send message to server
            String message = "Hello from UDP client!";
            byte[] data = message.getBytes();
            InetAddress serverAddress = InetAddress.getByName("localhost");
            DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, 8081);
            socket.send(packet);
            System.out.println("Sent to server: " + message);
            
            // Receive response from server
            byte[] buffer = new byte[1024];
            DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
            socket.receive(responsePacket);
            
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.println("Received from server: " + response);
            
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
        
        System.out.println("UDP Client disconnected");
    }
}